package fr.umlv.android.boggle.model;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Random;

import fr.umlv.android.boggle.Word;
import fr.umlv.android.boggle.listener.LetterEvent;

public class BoggleModelImpl extends AbstractBoggleModel {
	private final Letter[][] grid;
	private int nbCols = 4; // if change you need to update list dices
	private int nbRows = 4; // if change you need to update list dices
	private int score=0;
	private final List<String> wordsFound;
	private final Word word;
	private final List<Dice> dices;
	private final HashSet<String> dictionary;
	private boolean gameIsRunning = false;
	
	public BoggleModelImpl(InputStream inputStreamDico) {
		dictionary = new HashSet<String>();
		initDictionary(inputStreamDico);
		wordsFound = new ArrayList<String>();
		grid = new Letter[nbCols][nbRows];
		word = new Word();
		dices = new ArrayList<Dice>(nbCols*nbRows);
		dices.add(new Dice("ETUKNO"));
		dices.add(new Dice("EVGTIN"));
		dices.add(new Dice("DECAMP"));
		dices.add(new Dice("IELRUW"));
		dices.add(new Dice("EHIFSE"));
		dices.add(new Dice("RECALS"));
		dices.add(new Dice("ENTDOS"));
		dices.add(new Dice("OFXRIA"));
		dices.add(new Dice("NAVEDZ"));
		dices.add(new Dice("EIOATA"));
		dices.add(new Dice("GLENYU"));
		dices.add(new Dice("BMAQJO"));
		dices.add(new Dice("TLIBRA"));
		dices.add(new Dice("SPULTE"));
		dices.add(new Dice("AIMSOR"));
		dices.add(new Dice("ENHRIS"));
	}
	
	private void initDictionary(InputStream inputStreamDico) {
		InputStreamReader ipsr = new InputStreamReader(inputStreamDico);
		BufferedReader br = new BufferedReader(ipsr);
		String ligne;
		
		try { 			
			while ((ligne=br.readLine())!=null){
				dictionary.add(ligne);
			}
			System.out.println("FILE CLOSE");
			br.close();
		} catch (Exception e) {
			e.printStackTrace();
		} 
	}

	@Override
	public void mixGrid() {
		if(gameIsRunning) {
			//do nothing
			return;
		}
		score = 0;
		wordsFound.clear();
		word.clear();
		Collections.shuffle(dices);
		Random random = new Random();
		int indexDice = 0;
		for(int i=0; i<countRow(); i++) {
			for(int j=0; j<countCol(); j++) {
				Dice dice = dices.get(indexDice);
				int indexFace = random.nextInt(6);
				char character = dice.getFace(indexFace);
				grid[i][j] = new Letter(character, i, j);
				indexDice++;
			}
		}
		gameIsRunning = true;
		fireGameStarted();
	}
	
	@Override
	public int countRow() {
		return nbRows;
	}
	
	@Override
	public int countCol() {
		return nbCols;
	}
	
	@Override
	public Letter getLetter(int row, int col) {
		return grid[row][col];
	}
	
	@Override
	public void selectLetter(int row, int col) {
		Letter letter = grid[row][col];
		selectLetter(letter);
	}
	
	@Override
	public void selectLetter(Letter letter) {
		Letter lastLetterSelected = word.lastLetter();
		
		// if letter selected is the last letter selected 
		if(word.size()>2 && letter.equals(lastLetterSelected)) {
			// test the word
			// if word already found
			if(wordsFound.contains(word.toString())) {
				// emettre un son pour indiquer que le mot a deja ete trouve
			}
			else if(checkWord(word.toString())) {
				updateScore();
				wordsFound.add(word.toString());
				fireWordFound(word.toString());
				clearWord();
			}
			else { 
				// emettre un son pour indiquer que le mot est faux
			}
		}
		else {
			// if letter is already selected
			if(letter.isSelected()) {
				// si is the first letter -> unselect all letters
				if(word.size()>0 && letter.equals(word.firstLetter())) {
					clearWord();
				}
				else {
					clearWordAfter(letter);
				}
			}
			else {
				if(word.size()>0) {
					int i = lastLetterSelected.getRow();
					int j = lastLetterSelected.getCol();
					if( letter.getRow()< i-1  || letter.getRow()> i+1
						|| letter.getCol() < j-1 || letter.getCol()> j+1) {
						// do nothing
						return;
					}
				}
				if(letter.select()) {
					word.addLetter(letter);
					fireLetterChanged(letter, LetterEvent.LETTER_CHANGED);
				}
			}
		}
	}

	private void updateScore() {
		int length = word.size();
		int value = 0;
		if(length < 5) {
			value = 1;
		}
		else if(length == 5) {
			value = 2;
		}
		else if(length == 6) {
			value = 3;
		}
		else if(length == 7) {
			value = 5;
		}
		else if(length >= 8) {
			value = 11;
		}
		score += value;
	}
	

	private void clearWord() {
		clearWord(0);
	}

	private void clearWordAfter(Letter letter) {
		int index = word.indexOf(letter);
		clearWord(index+1);
	}
	
	private void clearWord(int start) {
		// unselect letters form index start 
		for(int i=start; i<word.size(); i++) {
			Letter letterSelected = word.getLetter(i);
			if(letterSelected.unselect()) {
				fireLetterChanged(letterSelected, LetterEvent.LETTER_CHANGED);
			}
		}
		word.clear(start);
	}

	
	/**
	 *  test if word is valid
	 * @param word 
	 * @return true if the word is valid
	 */
	private boolean checkWord(String word) {
		return dictionary.contains(word);
	}

	@Override
	public int countWordsFound() {
		return wordsFound.size();
	}
	
	@Override
	public String getWordFound(int index) {
		return wordsFound.get(index);
	}

	@Override
	public int getScore() {
		return score;
	}

	@Override
	public boolean isRunning() {
		return gameIsRunning;
	}
}
